package cc.bukkitPlugin.banitem.check;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

import org.bukkit.Material;
import org.bukkit.command.CommandSender;

import cc.bukkitPlugin.banitem.BanItem;
import cc.bukkitPlugin.banitem.BanItemEnume.ListType;
import cc.bukkitPlugin.banitem.command.analyse.AnalyseResult;
import cc.bukkitPlugin.banitem.manager.ItemManager;
import cc.bukkitPlugin.util.plugin.ABukkitPlugin;
import cc.bukkitPlugin.util.plugin.fileManager.AFileManager;

public class WorldTypeItem extends AFileManager<BanItem>{

    private final HashMap<Material,DamageList> mItems=new HashMap<>();
    private ItemManager mItemMan;

    private final ListType mListType;
    private final SimpleWorld mWorld;

    public WorldTypeItem(ItemManager pItemMan,SimpleWorld pWorld,ListType pListType){
        super(pItemMan.getPlugin(),WorldTypeItem.getFile(pWorld,pListType),"1.0");

        this.mItemMan=pItemMan;
        this.mListType=pListType;
        this.mWorld=pWorld;
    }

    @Override
    public boolean reloadConfig(CommandSender pSender){
        if(!super.reloadConfig(pSender)){
            ABukkitPlugin.severe(pSender,this.mWorld+"世界的"+this.mListType.getDisplayName()+"加载失败");
            return false;
        }
        this.mItems.clear();
        this.mItems.putAll(this.mItemMan.loadItemFromConfig(this.mConfig,this.mListType));
        ABukkitPlugin.info(pSender,this.mWorld+"世界"+": "+this.mListType.getDisplayName()+"载入"+this.mItems.size()+"个物品");
        return true;
    }

    public DamageList getDamageList(Material pMate){
        return this.mItems.get(pMate);
    }

    /**
     * 获取世界名,如果为默认配置,将返回"全局"
     */
    public String getNamedWorld(){
        return this.mWorld.toString();
    }

    public ListType getListType(){
        return this.mListType;
    }

    /**
     * 检查对应配置的物品是否在相应的列表中,函数中会发送消息
     * 
     * <p>
     * 当检查项的damage为-1,如果列表中存在对应的Item且damage中存在除-1外的其他项,认为不存在该配置
     * 当检查项的damage不是-1,如果列表中存在对应的Item且damage中存在-1,认为存在该配置
     * </p>
     * @param pCmdParam 相应的物品配置
     * @return 检查的结果,非null
     */
    public AnalyseResult test(CommandSender pSender,AnalyseResult pCmdParam){
        AnalyseResult tCmdParam=new AnalyseResult();
        tCmdParam.mListType=this.mListType;
        tCmdParam.mWorld=this.mWorld;
        DamageList tDamageList=this.mItems.get(pCmdParam.mItem);
        if(tDamageList==null)
            return tCmdParam.noItemFound(pCmdParam.mItem);

        tCmdParam.mItem=pCmdParam.mItem;
        ArrayList<Damage> checkDamages=new ArrayList<>();
        Damage td=tDamageList.get((short)-1);
        if(td!=null)
            checkDamages.add(td);
        if(pCmdParam.mDamage!=-1&&(td=tDamageList.get(pCmdParam.mDamage))!=null)
            checkDamages.add(td);
        if(checkDamages.isEmpty())
            return tCmdParam.noDamageFound(pCmdParam.mDamage);
        for(Damage sDamage : checkDamages){
            tCmdParam.mDamage=sDamage.mValue;
            if(sDamage.isSimpleNode())
                continue;
            tCmdParam.mReason=sDamage.getReason(); // 复制原因
            if(sDamage.isEmpty())
                return tCmdParam.setFind(true);
            if(pCmdParam.mNBTLabel==null){ // 不检查NBT约束
                tCmdParam.setUnfitReason("此ID物品当Damage为"+sDamage.mValue+"时存在NBT约束,但是测试物品未设置NBT");
                continue;
            }
            HashSet<String> nbtValues=sDamage.getRawNBTValues(pCmdParam.mNBTLabel);
            if(nbtValues==null){ // 找不到对应的NBT标签
                tCmdParam.noNBTLabelFound(pCmdParam.mNBTLabel);
                continue;
            }
            tCmdParam.mNBTLabel=pCmdParam.mNBTLabel;
            if(nbtValues.contains("*")){
                tCmdParam.mNBTValue="*";
            }else if(nbtValues.contains(pCmdParam.mNBTValue)){
                tCmdParam.mNBTValue=pCmdParam.mNBTValue;
            }else{ // 找不到对应的NBT值
                tCmdParam.noNBTValueFound(pCmdParam.mNBTLabel,pCmdParam.mNBTValue);
                continue;
            }
            return tCmdParam.setFind(true);
        }
        return tCmdParam;
    }

    /**
     * 检查对应配置的物品是否在相应的列表中,函数中会发送消息
     * 
     * <p>
     * 当检查项的damage为-1,如果列表中存在对应的Item且damage中存在除-1外的其他项,认为不存在该配置
     * 当检查项的damage不是-1,如果列表中存在对应的Item且damage中存在-1,认为存在该配置
     * </p>
     * @param pCmdParam 相应的物品配置
     * @return 检查的结果,非null
     */
    public AnalyseResult check(CommandSender pSender,AnalyseResult pCmdParam){
        AnalyseResult tCmdParam=new AnalyseResult();
        tCmdParam.mListType=this.mListType;
        tCmdParam.mWorld=this.mWorld;

        DamageList tDamageList=this.mItems.get(pCmdParam.mItem);
        if(tDamageList==null)
            return tCmdParam.noItemFound(pCmdParam.mItem);
        tCmdParam.mItem=pCmdParam.mItem;
        if(pCmdParam.mDamage==null) //移除整个物品
            return tCmdParam.setFind(true);
        Damage nbtLabels=tDamageList.get(pCmdParam.mDamage);
        if(nbtLabels==null) // 找不到对应的damage
            return tCmdParam.noDamageFound(pCmdParam.mDamage);
        tCmdParam.mDamage=pCmdParam.mDamage;
        if(nbtLabels.isSimpleNode())
            return tCmdParam.setFind(true);
        tCmdParam.mReason=nbtLabels.getReason(); //不进行原因比较
        if(pCmdParam.mNBTLabel==null) // 不检查NBT约束
            return tCmdParam.setFind(true);
        HashSet<String> nbtValues=nbtLabels.getRawNBTValues(pCmdParam.mNBTLabel);
        if(nbtValues==null) // 找不到对应的NBT标签
            return tCmdParam.noNBTLabelFound(pCmdParam.mNBTLabel);
        tCmdParam.mNBTLabel=pCmdParam.mNBTLabel;
        if(pCmdParam.mNBTValue==null) //不检查NBT值约束
            return tCmdParam.setFind(true);
        if(nbtValues.contains(pCmdParam.mNBTValue)) // 找到对应的NBT值
            return tCmdParam.setFind(true);
        else return tCmdParam.noNBTValueFound(pCmdParam.mNBTLabel,pCmdParam.mNBTValue);

    }

    /**
     * 添加对应的物品到列表,如果已经存在,函数内会报错
     * @param pSender 谁
     * @param pCmdParam 物品配置
     * @return null或添加的物品配置
     */
    public AnalyseResult add(CommandSender pSender,AnalyseResult pCmdParam){
        AnalyseResult tCmdParam=this.check(pSender,pCmdParam);
        if(tCmdParam.mFind){
            ABukkitPlugin.warn(pSender,"&c以下配置项已经存在于"+this.mWorld+"世界的"+this.mListType.getDisplayName()+"中");
            tCmdParam.sendInfo(pSender,"&c  |");
            return null;
        }
        tCmdParam=new AnalyseResult();
        tCmdParam.mListType=this.mListType;
        tCmdParam.mWorld=this.mWorld;
        tCmdParam.mItem=pCmdParam.mItem;
        tCmdParam.mDamage=pCmdParam.mDamage;

        DamageList tDamageList=this.mItems.get(pCmdParam.mItem);
        if(tDamageList==null){
            tDamageList=new DamageList(pCmdParam.mItem);
            this.mItems.put(pCmdParam.mItem,tDamageList);
        }
        Damage tDamage=tDamageList.get(pCmdParam.mDamage);
        if(tDamage==null){ // 查找damage插入位置
            tDamage=new Damage(pCmdParam.mDamage,pCmdParam.mListType);
            tDamageList.add(tDamage);
        }
        if(!tDamage.isSimpleNode()){
            tDamage.setReason(pCmdParam.mReason);
            tCmdParam.mReason=tDamage.getReason();
            if(pCmdParam.mNBTLabel!=null){ // 查找NBT标签插入位置
                tDamage.putNBTValue(pCmdParam.mNBTLabel,pCmdParam.mNBTValue);
                tCmdParam.mNBTLabel=pCmdParam.mNBTLabel;
                tCmdParam.mNBTValue=pCmdParam.mNBTValue;
            }
        }

        this.saveConfig(pSender);
        ABukkitPlugin.send(pSender,"已经添加该物品配置到"+this.mWorld+"世界的"+this.mListType.getDisplayName()+"中");
        tCmdParam.sendInfo(pSender);
        return tCmdParam;
    }

    /**
     * 添加对应的物品到列表,如果已经存在,函数内会报错
     * @param pSender 谁
     * @param pCmdParam 物品配置
     * @return null或者被移除的配置
     */
    public AnalyseResult remove(CommandSender pSender,AnalyseResult pCmdParam){
        AnalyseResult tCmdParam=this.check(pSender,pCmdParam);
        if(!tCmdParam.mFind){
            ABukkitPlugin.warn(pSender,"&c未在"+this.mWorld+"世界的"+this.mListType.getDisplayName()+"中找到以下配置项");
            tCmdParam.sendInfo(pSender,"&c  |");
            return null;
        }
        tCmdParam=new AnalyseResult();
        tCmdParam.mListType=this.mListType;
        tCmdParam.mWorld=this.mWorld;
        tCmdParam.mItem=pCmdParam.mItem;

        if(pCmdParam.mDamage==null){ //移除整个Item节点
            this.mItems.remove(pCmdParam.mItem);
        }else{
            tCmdParam.mDamage=pCmdParam.mDamage;
            DamageList tDamageList=this.mItems.get(pCmdParam.mItem);
            Damage tDamage=tDamageList.get(pCmdParam.mDamage);
            if(tDamage.isSimpleNode()||pCmdParam.mNBTLabel==null){ //damage是一个simple Node或 忽视NBTLabel的检查
                tDamageList.remove(pCmdParam.mDamage);
            }else{
                tCmdParam.mNBTLabel=pCmdParam.mNBTLabel;
                tCmdParam.mReason=tDamage.getReason();

                if(pCmdParam.mNBTValue==null){ //移除整个NBT标签节点
                    tDamage.removeNBTLabel(pCmdParam.mNBTLabel);
                }else{
                    tCmdParam.mNBTValue=pCmdParam.mNBTValue;
                    tDamage.removeNBTValue(pCmdParam.mNBTLabel,pCmdParam.mNBTValue);
                }
                if(tDamage.isEmpty())
                    tDamageList.remove(pCmdParam.mDamage);
            }
            if(tDamageList.isEmpty())
                this.mItems.remove(pCmdParam.mItem);
        }

        this.saveConfig(pSender);
        ABukkitPlugin.send(pSender,"已经从"+this.mWorld+"世界的"+this.mListType.getDisplayName()+"中移除以下物品配置");
        tCmdParam.sendInfo(pSender);
        return tCmdParam;
    }

    @Override
    public boolean saveConfig(CommandSender pSender){
        this.mItemMan.saveItemToConfig(this.mConfig,this.mItems);
        return super.saveConfig(pSender);
    }

    private static String getFile(SimpleWorld pWorld,ListType pListType){
        if(pWorld==null)
            throw new NullPointerException("世界key不能为null");
        if(pWorld.getRawWorld()==null)
            return pListType.getListName()+".yml";
        else return ItemManager.WORLD_ITEM_DIR+pWorld+File.separator+pListType.getListName()+".yml";
    }

}
